From c098a9a7a919e002436cf7b08cbf53f2ef906918 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 23 Sep 2014 07:30:16 -0700 Subject: [PATCH] Allow updating to a precise revision This commit adds a flag, --precise, to cargo update. This flag is used to update a dependency to precisely an exact revision (or branch) as part of an update step. For git repositories the argument is some form of reference, while registry packages this will be a version number. The flag --precise forces a non-aggressive update and will fail if the --aggresive flag is specified. Closes #484 --- src/bin/update.rs | 19 +++++++++-- src/cargo/ops/cargo_generate_lockfile.rs | 41 +++++++++++++++++------- src/cargo/ops/mod.rs | 1 + src/cargo/util/config.rs | 4 +-- tests/test_cargo_compile_git_deps.rs | 10 ++++++ 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/bin/update.rs b/src/bin/update.rs index f2328a23a..d7fd67b5f 100644 --- a/src/bin/update.rs +++ b/src/bin/update.rs @@ -15,6 +15,7 @@ Usage: Options: -h, --help Print this message --aggressive Force updating all dependencies of as well + --precise PRECISE Update a single dependency to exactly PRECISE --manifest-path PATH Path to the manifest to compile -v, --verbose Use verbose output @@ -27,18 +28,32 @@ updated. Its transitive dependencies will be updated only if cannot be updated without updating dependencies. All other dependencies will remain locked at their currently recorded versions. +If PRECISE is specified, then --aggressive must not also be specified. The +argument PRECISE is a string representing a precise revision that the package +being updated should be updated to. For example, if the package comes from a git +repository, then PRECISE would be the exact revision that the repository should +be updated to. + If is not given, then all dependencies will be re-resolved and updated. For more information about package ids, see `cargo help pkgid`. -", flag_manifest_path: Option, arg_spec: Option) +", flag_manifest_path: Option, arg_spec: Option, + flag_precise: Option) pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { debug!("executing; cmd=cargo-update; args={}", os::args()); shell.set_verbose(options.flag_verbose); let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path)); - ops::update_lockfile(&root, shell, options.arg_spec, options.flag_aggressive) + let mut update_opts = ops::UpdateOptions { + aggressive: options.flag_aggressive, + precise: options.flag_precise.as_ref().map(|s| s.as_slice()), + to_update: options.arg_spec.as_ref().map(|s| s.as_slice()), + shell: shell, + }; + + ops::update_lockfile(&root, &mut update_opts) .map(|_| None).map_err(|err| CliError::from_boxed(err, 101)) } diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs index b2132e047..f9cde510c 100644 --- a/src/cargo/ops/cargo_generate_lockfile.rs +++ b/src/cargo/ops/cargo_generate_lockfile.rs @@ -12,6 +12,13 @@ use util::config::{Config}; use util::{CargoResult, human}; use util::toml as cargo_toml; +pub struct UpdateOptions<'a> { + pub shell: &'a mut MultiShell<'a>, + pub to_update: Option<&'a str>, + pub precise: Option<&'a str>, + pub aggressive: bool, +} + pub fn generate_lockfile(manifest_path: &Path, shell: &mut MultiShell) -> CargoResult<()> { @@ -33,9 +40,7 @@ pub fn generate_lockfile(manifest_path: &Path, } pub fn update_lockfile(manifest_path: &Path, - shell: &mut MultiShell, - to_update: Option, - aggressive: bool) -> CargoResult<()> { + opts: &mut UpdateOptions) -> CargoResult<()> { let mut source = try!(PathSource::for_path(&manifest_path.dir_path())); try!(source.update()); let package = try!(source.get_root_package()); @@ -47,23 +52,37 @@ pub fn update_lockfile(manifest_path: &Path, None => return Err(human("A Cargo.lock must exist before it is updated")) }; - let mut config = try!(Config::new(shell, None, None)); + if opts.aggressive && opts.precise.is_some() { + return Err(human("cannot specify both aggressive and precise \ + simultaneously")) + } + + let mut config = try!(Config::new(opts.shell, None, None)); let mut registry = PackageRegistry::new(&mut config); - let sources = match to_update { + let mut sources = Vec::new(); + match opts.to_update { Some(name) => { let mut to_avoid = HashSet::new(); - let dep = try!(resolve.query(name.as_slice())); - if aggressive { + let dep = try!(resolve.query(name)); + if opts.aggressive { fill_with_deps(&resolve, dep, &mut to_avoid); } else { to_avoid.insert(dep); + match opts.precise { + Some(precise) => { + sources.push(dep.get_source_id().clone() + .with_precise(precise.to_string())); + } + None => {} + } } - resolve.iter().filter(|pkgid| !to_avoid.contains(pkgid)) - .map(|pkgid| pkgid.get_source_id().clone()).collect() + sources.extend(resolve.iter() + .filter(|p| !to_avoid.contains(p)) + .map(|p| p.get_source_id().clone())); } - None => package.get_source_ids(), - }; + None => sources.extend(package.get_source_ids().into_iter()), + } try!(registry.add_sources(sources)); let resolve = try!(resolver::resolve(package.get_summary(), diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index 67e30def8..c3c584ac3 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -7,6 +7,7 @@ pub use self::cargo_new::{new, NewOptions}; pub use self::cargo_doc::{doc, DocOptions}; pub use self::cargo_generate_lockfile::{generate_lockfile, write_resolve}; pub use self::cargo_generate_lockfile::{update_lockfile, load_lockfile}; +pub use self::cargo_generate_lockfile::UpdateOptions; pub use self::cargo_test::{run_tests, run_benches, TestOptions}; pub use self::cargo_package::package; pub use self::cargo_upload::{upload, upload_configuration, UploadConfig}; diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 0d21145a2..5610369af 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -144,13 +144,13 @@ impl ConfigValue { toml::String(val) => Ok((val, path.clone())), _ => Err(internal("")), } - }).collect::>()))) + }).collect::>()))) } toml::Table(val) => { Ok(Table(try!(val.into_iter().map(|(key, value)| { let value = raw_try!(ConfigValue::from_toml(path, value)); Ok((key, value)) - }).collect::>()))) + }).collect::>()))) } _ => return Err(internal("")) } diff --git a/tests/test_cargo_compile_git_deps.rs b/tests/test_cargo_compile_git_deps.rs index 34eeadd18..283113bac 100644 --- a/tests/test_cargo_compile_git_deps.rs +++ b/tests/test_cargo_compile_git_deps.rs @@ -681,13 +681,23 @@ test!(update_with_shared_deps { pub fn bar() { println!("hello!"); } "#).assert(); let repo = git2::Repository::open(&git_project.root()).unwrap(); + let old_head = repo.head().unwrap().target().unwrap(); add(&repo); commit(&repo); timer::sleep(Duration::milliseconds(1000)); + // By default, not transitive updates assert_that(p.process(cargo_dir().join("cargo")).arg("update").arg("dep1"), execs().with_stdout("")); + + // Specifying a precise rev to the old rev shouldn't actually update + // anything because we already have the rev in the db. + assert_that(p.process(cargo_dir().join("cargo")).arg("update").arg("bar") + .arg("--precise").arg(old_head.to_string()), + execs().with_stdout("")); + + // Updating aggressively should, however, update the repo. assert_that(p.process(cargo_dir().join("cargo")).arg("update").arg("dep1") .arg("--aggressive"), execs().with_stdout(format!("{} git repository `{}`", -- 2.30.2